﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Management;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Collector.Device;

namespace Collector.Certified
{
    class CertifiedMain
    {
        private string TAG = "CertifiedMain:";
        private RunMode mMode = RunMode.eMODE_RUN_USER;
        private ComPortPack mSerialPort = null;
        private PulseGeneralCollector mGeneralCollector = new PulseGeneralCollector();
        private DeviceCommunicationManager mDCManager = new DeviceCommunicationManager();
        private PulseApplicantion mApplication = new PulseApplicantion();
        // 配置定时器
        System.Timers.Timer mSensorTimer = new System.Timers.Timer(100);
        System.Timers.Timer mTestTimer = new System.Timers.Timer(1000);
        private int mTimeCount = 0;

        public delegate int FormNotifyDelegate<T>(T obj, PulseCommonHelper.Display.DisplayType type);
        public FormNotifyDelegate<PulseCommonHelper.Display> notifyCertifiedForm;

        public enum RunMode
        {
            eMODE_RUN_FACTORY,
            eMODE_RUN_USER,
            eMODE_RUN_CERTIFIED
        }

        public RunMode Mode
        {
            set { mMode = value; }
            get { return mMode;  }
        }

        public bool startCertified()
        {
            PulseCommonHelper.Display display = new PulseCommonHelper.Display();
            bool ret = false;
            if (InitSerialPort() == false)
            {
                return false;
            }

            if (!InitializePulseDevice())
            {
                return false;
            }

            /**
             * 版本号检查
             */
            string MCUTargetVersion, MCUConfigVersion;
            // 从配置文件中读取设备的测试目标版本号
            FileConfig.getmmHgListInfo("CheckMcuVersion", out MCUConfigVersion);
            // 从设备读取固件版本号            
            ret = mDCManager.ReadMcuVersion(out MCUTargetVersion);
            if (!ret)
            {
                display.Message = "Read Mcu Version err";
            } else
            {
                ret = MCUConfigVersion.Trim() == MCUTargetVersion.Trim();
                display.Message = string.Format("Read:{0},config:{1}", MCUTargetVersion, MCUConfigVersion);
            }

            if (notifyCertifiedForm != null)
            {                                
                display.LogLevel = (ret)? LogMsgType.INFO : LogMsgType.ERROR;
                notifyCertifiedForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_MSG);
            }

            //if (!ret) return ret;

            /**
             * 读取设备SN
             */
            string deviceSerialNumber;
            if (!(ret = mDCManager.ReadSerialNumber(out deviceSerialNumber)))
            {
                display.Message = "Read MCU SerialNumber Fail";
            } else
            {
                display.Message = "Get SN :" + deviceSerialNumber;
            }

            if (notifyCertifiedForm != null)
            {
                display.LogLevel = (ret) ? LogMsgType.INFO : LogMsgType.ERROR;
                notifyCertifiedForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_MSG);
            }

            if (!ret) return ret;

            /**
             * 读取基线压力
             */
            double[] baseline_value = new double[3];
            ret = mDCManager.ReadInitialPPressure(ref baseline_value);
            if (!ret)
            {
                display.Message = "读取初始静压失败";
                display.LogLevel = (ret) ? LogMsgType.INFO : LogMsgType.ERROR;
                notifyCertifiedForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_MSG);
                return ret;
            } 
            else
            {
                if (notifyCertifiedForm != null)
                {
                    CertifiedIntention intent = new CertifiedIntention();
                    intent.Type = CertifiedIntention.IntentionType.eINTENT_TYPE_UPDATE_BASELINE;                    
                    intent.Data = baseline_value;
                    display.UserData = intent;
                    display.LogLevel = (ret) ? LogMsgType.INFO : LogMsgType.ERROR;
                    notifyCertifiedForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_USER);
                }
            }

            /**
             * 开启脉压和气压采集
             */
            byte[] temp;
            if (!mDCManager.SendCommandToDevice("SPStartPulsePressure", 0x64, out temp)) { 
                //throw new Exception("开启脉压收集失败");
                Log.Error(TAG + "开启脉压收集失败");
                return false;
            }
            if (!mDCManager.SendCommandToDevice("SPStartAirPressure", 0x95, out temp))
            {
                //throw new Exception("开启气压收集失败");
                Log.Error(TAG + "开启气压收集失败");
                return false;
            }

            return true;
        }

        public bool stopCertified()
        {
            ResetDeviceStatus();
            ReleaseSerialPort();
            return true;
        }


        private bool InitSerialPort()
        {
            bool isComPortOpen = false;
            string portInfo = "";
            string portName = "";

            using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from Win32_PnPEntity"))
            {
                var hardInfos = searcher.Get();
                foreach (var hardInfo in hardInfos)
                {
                    portInfo = hardInfo.Properties["DeviceID"].Value.ToString();
                    if (!string.IsNullOrEmpty(portInfo) && portInfo.Contains("VID_28E9&PID_018A"))
                    {
                        //string strComName = hardInfo.Properties["Name"].Value.ToString();
                        portName = Regex.Replace(hardInfo.Properties["Name"].Value.ToString(), @"(.*\()(.*)(\).*)", "$2").Trim();
                        Log.Debug(string.Format("DeviceName:{0},DeviceID:{1}", portName, portInfo));//打印串口设备名称及串口号
                    }
                }
            }

            if(string.IsNullOrEmpty(portName))
            {
                Log.Debug(TAG + "No found serialport.");
                return isComPortOpen;
            }

            // 配置串口，按照115200-8-1-无的方式配置
            ComPortPack.SerialPortModel model = new ComPortPack.SerialPortModel();
            model.PortName = portName;
            model.BaudRate = 115200;
            model.Parity = Parity.None;
            model.StopBits = StopBits.One;
            model.DataBits = 8;

            mSerialPort = new ComPortPack(model);

            // open SerPort
            if (!mSerialPort.Open())
                return false;

            isComPortOpen = mSerialPort.IsOpen();
            // Default set general collector.
            mSerialPort.collector = mGeneralCollector;

            if (notifyCertifiedForm != null)
            {
                PulseCommonHelper.Display display = new PulseCommonHelper.Display();
                display.Message = string.Format("comPort {0} Open Status:{1}", portName, isComPortOpen);
                notifyCertifiedForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_MSG);
                if(isComPortOpen)
                {
                    CertifiedIntention intention = new CertifiedIntention("测试串口 :" + portName, CertifiedIntention.IntentionType.eINTENT_TYPE_UPDATE_SERIALPORT_LABLE);
                    display.UserData = intention;
                    notifyCertifiedForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_USER);
                }
            }

            mTimeCount = 0;
            timerCreateTotalTest(mTestTimer);

            return isComPortOpen;
        }

        /// <summary>
        /// 释放脉象设备端口
        /// </summary>
        private void ReleaseSerialPort()
        {
            if (null != mSerialPort)
            {
                Log.Debug(TAG + "ReleaseSerialPort Release");
                mSerialPort.Received -= mDCManager.SerialPortReceivedCallback;
                //RemoveEvent<ComPortPack>(mSerialPort, "Received");
                if (mSerialPort.IsOpen())
                {
                    Log.Debug(TAG + "ReleaseSerialPort close serialport");
                    mSerialPort.Close();
                    mSerialPort = null;
                }
            }

            timerSensorDesetting(mSensorTimer);
            timerDeleteTotalTest(mTestTimer);
        }

        /// <summary>
        /// 脉象初始化准备
        /// </summary>
        /// <returns></returns>
        private bool InitializePulseDevice()
        {
            bool ret = false;

            // 重置 脉象协议处理类配置
            mDCManager.ResetConfig();
            mDCManager.DeviceConnector = mSerialPort;
            // 用于接收实时脉压和气压值
            mDCManager.DeliveredCallback = DeliveredPulsePressureDataCallback;
            // 设置serialport received callback function
            mSerialPort.Received += mDCManager.SerialPortReceivedCallback;

            // 打开脉象设备灯，保持长亮
            mDCManager.OpenDeviceLed();

            // 请求复位设备
            ret = mDCManager.ResetDevice();

            //开始定时器，100ms频次显示当前脉压值和脉压差值
            timerSensorSetting(mSensorTimer);

            return ret;
        }

        private void ResetDeviceStatus()
        {
            byte[] payload;
            if (null == mSerialPort)
            {
                Log.Warn("ResetDeviceStatus:Pulse Serialport is released");
                return;
            }

            if (!mDCManager.SendCommandToDevice("SPStopPulsePressure", 0x74, out payload))
            {
                Log.Error("ResetDeviceStatus:Stop pulse pressure collect failed");
            }

            if (!mDCManager.SendCommandToDevice("SPStopAirPressure", 0x96, out payload))
            {
                Log.Error("ResetDeviceStatus:Stop air pressure collect failed");
            }

            if (!mDCManager.ResetDevice())
            {
                Log.Error("ResetDeviceStatus:Reset device status failed");
            }
            else
            {
                Log.Info("ResetDeviceStatus:Reset device status Successfuly");
            }
        }

        private bool DeliveredPulsePressureDataCallback(byte[] frame, double data, PulseProtocolPackage.PressureType type)
        {
            PulseCommonHelper.Display display = new PulseCommonHelper.Display();
            switch (type)
            {
                case PulseProtocolPackage.PressureType.ePRESSURE_TYPE_PULSE:
                {
                        if (FileConfig.PulseThreeBodyTest())
                        {
                            mApplication.pushPulseData(frame, ProductType.ePRODUCT_TYPE_THREE);

                            // Update scope plotlab value
                            var pluseDataMap = mApplication.PluseData;
                            if (notifyCertifiedForm != null)
                            {
                                CertifiedIntention intention = new CertifiedIntention();
                                intention.Type = CertifiedIntention.IntentionType.eINTENT_TYPE_UPDATE_SENSOR_DATA;
                                intention.PressureType = PulseProtocolPackage.PressureType.ePRESSURE_TYPE_PULSE;
                                foreach (var it in PulseApplicantion.TBPIndexMap)
                                {
                                    intention.AverageData[it.Key] = Math.Round(pluseDataMap[it.Value].Last(), 2);
                                }
                                display.UserData = intention;
                                notifyCertifiedForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_USER);
                            }
                        }
                        else
                        {
                            mApplication.pushPulseData(frame, ProductType.ePRODUCT_TYPE_SINGLE);
                            // Update scope plotlab value
                            var pluseDataMap = mApplication.PluseData;

                            if (notifyCertifiedForm != null)
                            {
                                CertifiedIntention intention = new CertifiedIntention();
                                intention.Type = CertifiedIntention.IntentionType.eINTENT_TYPE_UPDATE_SENSOR_DATA;
                                intention.PressureType = PulseProtocolPackage.PressureType.ePRESSURE_TYPE_PULSE;
                                intention.AverageData[0] = Math.Round(pluseDataMap[BodyPart.eBODY_PART_CUN].Last(), 2);
                                display.UserData = intention;
                                notifyCertifiedForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_USER);
                            }
                        }
                }
                break;
                case PulseProtocolPackage.PressureType.ePRESSURE_TYPE_AIR:
                {
                    if (notifyCertifiedForm != null)
                    {
                        CertifiedIntention intention = new CertifiedIntention();
                        intention.Type = CertifiedIntention.IntentionType.eINTENT_TYPE_UPDATE_SENSOR_DATA;
                        intention.PressureType = PulseProtocolPackage.PressureType.ePRESSURE_TYPE_AIR;
                        intention.Data = (object)data;
                        display.UserData = intention;
                        notifyCertifiedForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_USER);
                    }
                }
                break;
            }

            return true;
        }

            private void timerCreateTotalTest(System.Timers.Timer t)
        {
            t.Elapsed += new System.Timers.ElapsedEventHandler(updateTestTotalTime);
            t.AutoReset = true;
            t.Enabled = true;
            t.Start();
        }

        private void timerDeleteTotalTest(System.Timers.Timer t)
        {
            t.Elapsed -= new System.Timers.ElapsedEventHandler(updateTestTotalTime);
            t.AutoReset = false;
            t.Enabled = false;
            t.Stop();
        }

        private void updateTestTotalTime(Object myObject, EventArgs myEventArgs)
        {
            mTimeCount++;
            TimeSpan temp = new TimeSpan(0, 0, mTimeCount);

            if (notifyCertifiedForm != null)
            {
                PulseCommonHelper.Display display = new PulseCommonHelper.Display();
                string message = string.Format("{0}{1:00}:{2:00}:{3:00}", "测试时长 :", temp.Hours, temp.Minutes, temp.Seconds);
                CertifiedIntention intention = new CertifiedIntention(message, CertifiedIntention.IntentionType.eINTENT_TYPE_REFRESH_TEST_TIME);
                display.UserData = intention;
                notifyCertifiedForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_USER);
            }
        }

        private void timerSensorSetting(System.Timers.Timer t)
        {
            t.Interval = 100;
            t.Elapsed += new System.Timers.ElapsedEventHandler(getGraphCurveValue);
            t.AutoReset = true;
            t.Enabled = true;
            t.Start();
        }

        private void timerSensorDesetting(System.Timers.Timer t)
        {
            if (t.Enabled)
            {
                t.Elapsed -= new System.Timers.ElapsedEventHandler(getGraphCurveValue);
                t.AutoReset = false;
                t.Enabled = false;
                t.Stop();
            }
        }

        private void getGraphCurveValue(Object myObject, EventArgs myEventArgs)
        {
            if(notifyCertifiedForm != null)
            {
                PulseCommonHelper.Display display = new PulseCommonHelper.Display();
                CertifiedIntention intention = new CertifiedIntention();
                intention.Type = CertifiedIntention.IntentionType.eINTENT_TYPE_UPDATE_GRAPH_CURVE;
                if (FileConfig.PulseThreeBodyTest())
                {
                    foreach(var it in PulseApplicantion.TBPIndexMap)
                    {
                        double ave = mApplication.PluseData[it.Value].Average();
                        intention.AverageData[it.Key] = ave;
                    }
                }
                else
                {
                    intention.AverageData[0] = Math.Round(mApplication.PluseData[BodyPart.eBODY_PART_CUN].Average(), 2);
                }

                display.UserData = intention;
                notifyCertifiedForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_USER);
            }
        }

        public class CertifiedIntention
        {
            private IntentionType type;
            private PulseProtocolPackage.PressureType pressureType;
            private object data;
            private string message;
            private double[] averageData = new double[3];

            public enum IntentionType
            {
                eINTENT_TYPE_UPDATE_SERIALPORT_LABLE,
                eINTENT_TYPE_REFRESH_TEST_TIME,
                eINTENT_TYPE_UPDATE_SENSOR_DATA,
                eINTENT_TYPE_UPDATE_GRAPH_CURVE,
                eINTENT_TYPE_UPDATE_BASELINE
            }

            public CertifiedIntention() { }

            public CertifiedIntention(string msg, IntentionType t)
            {
                message = msg;
                type = t;
            }

            public IntentionType Type
            {
                set { type = value; }
                get { return type; }
            }

            public string Message
            {
                set { message = value; }
                get { return message; }
            }

            public PulseProtocolPackage.PressureType PressureType
            {
                set { pressureType = value; }
                get { return pressureType; }
            }

            public object Data
            {
                set { data = value; }
                get { return data; }
            }

            public double[] AverageData
            {
                get { return averageData; }
            }
        }
    }
}
